<template>
  <view>
    <!-- #ifdef APP-NVUE -->
    <view @click="open"></view>
    <view :eventPenetrationEnabled="true">
      <slot name="trigger"></slot>
    </view>
    <!-- #endif -->
    <!-- #ifndef APP-NVUE -->
    <view @click="open">
      <slot name="trigger"></slot>
    </view>
    <!-- #endif -->

    <tm-overlay
      :inContent="props.inContent"
      ref="overlayAni"
      :duration="props.duration + 80"
      @open="OverLayOpen"
      @close="overclose"
      :zIndex="props.zIndex"
      :transparent="!props.mask"
      @click="overlayClickFun"
      :align="align_rp"
      :teleport="props.teleport"
      :overlayClick="false"
      v-model:show="_show"
      :fixd-top="fixdTop"
    >
      <tm-translate
        :reverse="reverse_rp"
        :width="anwidth"
        :height="anheight"
        ref="drawerANI"
        :auto-play="false"
        :name="aniname"
        :duration="props.duration + 80"
        @end="playEndEvent"
      >
        <view
          @click.stop="$event.stopPropagation()"
          :style="[
            { width: anwidth, height: anheight },
            !props.transparent ? tmcomputed.borderCss : '',
            !props.transparent ? tmcomputed.backgroundColorCss : '',
            !props.transparent ? tmcomputed.shadowColor : '',
            customCSSStyle,
            
          ]"
          :class="[round_rp, 'flex flex-col overflow ', customClass]"
        >
          <view
            v-if="!props.closeable && !props.hideHeader"
            class="flex flex-row flex-row-center-center flex-between px-24"
            style="height: 100rpx"
          >
            <view class="flex-4 flex-shrink">
              <tm-text
                v-if="!props.hideCancel && !loading && !ok_loading"
                @click="cancel"
                :label="props.cancelText"
              ></tm-text>
            </view>
            <view class="flex-8 px-32 flex-center">
              <slot name="title">
                <tm-text _class="text-overflow-1 opacity-7" :label="props.title"></tm-text>
              </slot>
            </view>
            <view class="flex-4 flex-shrink flex-row flex-row-center-end">
              <tm-text
                :color="okColor"
                @click="ok"
                v-if="!ok_loading && !loading"
                :dark="props.dark"
                :label="props.okText"
              ></tm-text>
              <tm-icon
                :color="okColor"
                v-if="ok_loading || loading"
                :spin="ok_loading || loading"
                :dark="isDark"
                :_class="isDark !== true ? 'opacity-4' : ''"
                :fontSize="48"
                :name="ok_loading || loading ? 'tmicon-shuaxin' : 'tmicon-times-circle-fill'"
              ></tm-icon>
            </view>
          </view>
          <view
            v-else-if="props.closeable && props.hideHeader"
            class="flex flex-row flex-row-center-center flex-between hb-py-3 hb-px-4"
            :class="{ 'hb-border-b-light-800 hb-border': bottomBorder }"
          >
            <view class="flex-4"></view>
            <view class="flex-8 flex-center" @click.stop>
              <slot name="title">
                <tm-text
                  _class="text-overflow-1 opacity-7"
                  :dark="props.dark"
                  :label="props.title"
                ></tm-text>
              </slot>
            </view>
            <view class="flex-4 flex-shrink flex-row flex-row-center-end">
              <tm-icon
                @click="cancel"
                :dark="props.dark"
                :_class="isDark !== true ? 'opacity-3' : ''"
                :fontSize="48"
                name="tmicon-times-circle-fill"
              ></tm-icon>
            </view>
          </view>
          <view v-else-if="!props.closeable && props.hideHeader">
            <slot name="customHeader" />
          </view>

          <!-- #ifdef APP-NVUE -->
          <scroll-view
            :scroll-y="!props.disabbleScroll"
            :style="[{ height: contentHeight }]"
            class="overflow"
          >
            <slot name="default"></slot>
          </scroll-view>
          <!-- #endif -->
          <!-- #ifndef APP-NVUE -->
          <view
            :style="{
              overflowY: props.disabbleScroll ? 'normal' : 'auto',
              height: contentHeight,
            }"
          >
            <slot name="default"></slot>
          </view>
          <!-- #endif -->
          <slot name="foot">
            <view v-if="props.footHeight > 0" class="flex"></view>
          </slot>
        </view>
      </tm-translate>
    </tm-overlay>
  </view>
</template>

<script lang="ts" setup>
  /**
   * 抽屉
   * @description 别名poup弹层，提供，左，右，上，下，中弹出内容。
   */
  import tmTranslate from '../tm-translate/tm-translate.vue';
  import tmText from '../tm-text/tm-text.vue';
  import tmIcon from '../tm-icon/tm-icon.vue';
  import tmOverlay from '../tm-overlay/tm-overlay.vue';
  import {
    getCurrentInstance,
    computed,
    ref,
    provide,
    inject,
    onMounted,
    onUnmounted,
    nextTick,
    watch,
  } from 'vue';
  import { cssstyle, tmVuetify, colorThemeType } from '../../tool/lib/interface';
  import {
    custom_props,
    computedTheme,
    computedClass,
    computedStyle,
    computedDark,
  } from '../../tool/lib/minxs';
  import { useTmpiniaStore } from '../../tool/lib/tmpinia';
  const drawerANI = ref<InstanceType<typeof tmTranslate> | null>(null);
  const overlayAni = ref<InstanceType<typeof tmOverlay> | null>(null);
  const store = useTmpiniaStore();
  const props = defineProps({
    ...custom_props,
    //是否显示遮罩
    mask: {
      type: [Boolean, String],
      default: true,
    },
    //抽屉放置的位置
    placement: {
      type: String,
      default: 'bottom', //top|left|right|bottom|center
    },
    show: {
      type: [Boolean],
      default: false,
    },
    width: {
      type: Number,
      default: 500,
    },
    height: {
      type: Number,
      default: 600,
    },
    round: {
      type: Number,
      default: 10,
    },
    //弹出的动画时间单位ms.
    duration: {
      type: Number,
      default: 300,
    },
    //是否允许点击遮罩关闭
    overlayClick: {
      type: Boolean,
      default: true,
    },
    transparent: {
      type: [Boolean, String],
      default: false,
    },
    //如果显示关闭。标题栏被替换为左标题右关闭按钮。
    closeable: {
      type: [Boolean, String],
      default: false,
    },
    color: {
      type: String,
      default: 'white',
    },
    title: [String],
    okText: {
      type: [String],
      default: '完成',
    },
    okColor: {
      type: [String],
      default: 'primary',
    },
    //true时，确认按钮将出现加载状态。
    okLoading: {
      type: [Boolean, String],
      default: false,
    },
    cancelText: {
      type: [String],
      default: '取消',
    },
    hideCancel: {
      type: [Boolean, String],
      default: false,
    },
    //隐藏工具栏，标题，取消，确认
    hideHeader: {
      type: [Boolean, String],
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    zIndex: {
      type: [Number, String],
      default: 401,
    },
    unit: {
      type: String,
      default: 'rpx',
    },
    disabbleScroll: {
      type: Boolean,
      default: false,
    },
    /** 是否嵌入弹层，开启后将在它的父组件内执行弹层。 */
    inContent: {
      type: Boolean,
      default: false,
    },
    footHeight: {
      type: Number,
      default: 0,
    },
    /** 是否使用teleport */
    teleport: {
      type: Boolean,
      default: true,
    },
    /**打开前执行 */
    beforeOpen: {
      type: Function,
      default: null,
    },
    /**关点击ok前执行，如果返回是false，将阻止关闭. */
    beforeOk: {
      type: Function,
      default: null,
    },
    /**点击取消前执行，如果返回fase将阻止关闭. */
    beforeCance: {
      type: Function,
      default: null,
    },
    fixdTop: {
      type: Number,
      default: 0,
    },
    bottomBorder: {
      type: Boolean,
      default: false,
    },
  });
  const emits = defineEmits(['click', 'open', 'close', 'update:show', 'ok', 'cancel']);
  const proxy = getCurrentInstance()?.proxy ?? null;
  const sysinfo = inject(
    'tmuiSysInfo',
    computed(() => {
      return {
        bottom: 0,
        height: 750,
        width: uni.upx2px(750),
        top: 0,
        isCustomHeader: false,
        sysinfo: null,
      };
    }),
  );
  // 设置响应式全局组件库配置表。
  const tmcfg = computed<tmVuetify>(() => store.tmStore);
  //自定义样式：
  const customCSSStyle = computed(() => computedStyle(props));
  //自定类
  const customClass = computed(() => computedClass(props));
  //是否暗黑模式。
  const isDark = computed(() => computedDark(props, tmcfg.value));
  //计算主题
  const tmcomputed = computed<cssstyle>(() => computedTheme(props, isDark.value, tmcfg.value));
  const syswidth = computed(() => sysinfo.value.width);
  const sysheight = computed(() => sysinfo.value.height);
  const reverse = ref(true);
  const timeid = ref(0);
  let timerId: any = NaN;
  let timerIdth: any = NaN;
  let timerIdth_flas = false;
  uni.hideKeyboard();
  let _show = ref(props.show);
  const isPlaying = ref(false);
  function debounce(func: Function, wait = 500, immediate = false) {
    // 清除定时器
    if (!isNaN(timerId)) clearTimeout(timerId);
    // 立即执行，此类情况一般用不到

    if (immediate) {
      timerId = setTimeout(() => {
        timerId = NaN;
      }, wait);
      typeof func === 'function' && func();
    } else {
      // 设置定时器，当最后一次操作后，timeout不会再被清除，所以在延时wait毫秒后执行func回调方法
      timerId = setTimeout(() => {
        typeof func === 'function' && func();
      }, wait);
    }
  }

  function throttle(func: Function, wait = 500, immediate = true) {
    if (immediate) {
      if (!timerIdth_flas) {
        timerIdth_flas = true;
        // 如果是立即执行，则在wait毫秒内开始时执行
        typeof func === 'function' && func();

        timerIdth = setTimeout(() => {
          timerIdth_flas = false;
        }, wait);
      }
    } else {
      if (!timerIdth_flas) {
        timerIdth_flas = true;
        // 如果是非立即执行，则在wait毫秒内的结束处执行
        timerIdth = setTimeout(() => {
          timerIdth_flas = false;
          typeof func === 'function' && func();
        }, wait);
      }
    }
  }

  timeid.value = uni.$tm.u.getUid(4);
  if (_show.value) {
    reverse.value = false;
  }
  watch(
    () => props.show,
    (val) => {
      if (val) {
        reverse.value = true;
      } else {
        reverse.value = false;
        overlayAni.value?.close();
      }
      if (_show.value !== props.show) {
        nextTick(() => {
          drawerANI.value?.play();
          isPlaying.value = true;
        });
      }
      _show.value = props.show;
    },
  );
  onMounted(() => {
    if (_show.value) {
      open();
    }
  });
  const ok_loading = computed(() => props.okLoading);
  const loading = ref(false);
  const round_rp = computed(() => {
    if (aniname.value == 'left') return 'round-r-' + props.round;
    if (aniname.value == 'right') return 'round-l-' + props.round;
    if (aniname.value == 'up') return 'round-b-' + props.round;
    if (aniname.value == 'down') return 'round-t-' + props.round;
    if (aniname.value == 'zoom') return 'round-' + props.round;
  });
  const reverse_rp = computed(() => {
    if (aniname.value != 'zoom') return reverse.value;
    return !reverse.value;
  });
  const aniname = computed(() => {
    if (props.placement == 'center') return 'zoom';
    if (props.placement == 'top') return 'up';
    if (props.placement == 'bottom') return 'down';
    return props.placement;
  });
  const anwidth = computed(() => {
    if (aniname.value == 'zoom') {
      return props.width + props.unit;
    }
    if (props.placement == 'left' || props.placement == 'right') {
      return props.width + props.unit;
    }
    return syswidth.value + 'px';
  });
  const anheight = computed(() => {
    let wucha = 0;
    if (props.placement == 'top' || props.placement == 'bottom' || aniname.value == 'zoom') {
      return props.height + wucha + props.unit;
    }
    return sysheight.value + 'px';
  });

  const contentHeight = computed(() => {
    const headerHeight = 44;
    let base_height = props.hideHeader ? 0 : headerHeight;
    if (useSlots().customHeader) {
      base_height = headerHeight;
    }
    let _footerHeight = uni.$tm.u.topx(props.footHeight);
    if (props.placement == 'top' || props.placement == 'bottom' || aniname.value == 'zoom') {
      let h = props.height;
      if (props.unit == 'rpx') {
        h = uni.upx2px(props.height);
      }
      return h - base_height - _footerHeight + 'px';
    }
    return sysheight.value - base_height - _footerHeight + 'px';
  });
  const align_rp = computed(() => {
    if (aniname.value == 'down') {
      return 'flex-col-bottom-center';
    }
    if (aniname.value == 'up') {
      return 'flex-top-custom';
    }
    if (aniname.value == 'left') {
      return 'flex-row-top-start';
    }
    if (aniname.value == 'right') {
      return 'flex-row-bottom-start';
    }
    if (aniname.value == 'zoom') {
      return 'flex-center';
    }
  });

  async function _beforeOpenFun() {
    if (typeof props.beforeOpen === 'function') {
      loading.value = true;
      let p = await props.beforeOpen();
      if (typeof p === 'function') {
        p = await p();
      }
      loading.value = false;
    }
  }

  async function _beforeOkFun() {
    let p = true;
    if (typeof props.beforeOk === 'function') {
      loading.value = true;
      p = await props.beforeOk();
      if (typeof p === 'function') {
        p = await p();
      }
      loading.value = false;
      if (!p) return;
    }
    return p;
  }

  async function _beforeCancelFun() {
    let p = true;
    if (typeof props.beforeCance === 'function') {
      loading.value = true;
      p = await props.beforeCance();
      if (typeof p === 'function') {
        p = await p();
      }
      loading.value = false;
      if (!p) return;
    }
    return p;
  }

  function OverLayOpen() {
    // nextTick(function() {
    // 	drawerANI.value?.play();
    // })
    _beforeOpenFun();
    _show.value = true;
    emits('open');
    emits('update:show', true);
  }
  function overclose() {
    nextTick(() => {
      _show.value = false;
      emits('close');
      emits('update:show', false);
    });
  }
  async function overlayClickFun(e: Event) {
    if (
      !props.overlayClick ||
      props.disabled ||
      !overlayAni.value ||
      loading.value ||
      isPlaying.value
    )
      return;
    emits('click', e);
    if (!(await _beforeCancelFun())) return;
    reverse.value = false;
    throttle(
      async () => {
        emits('cancel');
        overlayAni.value?.close();
        drawerANI.value?.play();
      },
      props.duration + 80,
      true,
    );
  }
  function playEndEvent() {
    isPlaying.value = false;
  }

  async function ok() {
    if (props.disabled || loading.value) return;
    if (!(await _beforeOkFun())) return;
    reverse.value = false;
    debounce(
      () => {
        emits('ok');
        overlayAni.value?.close();
        drawerANI.value?.play();
      },
      500,
      true,
    );
  }

  async function cancel() {
    if (props.disabled || loading.value) return;
    if (!(await _beforeCancelFun())) return;
    reverse.value = false;
    debounce(
      () => {
        emits('cancel');
        overlayAni.value?.close();
        drawerANI.value?.play();
      },
      500,
      true,
    );
  }

  //外部调用。
  function open() {
    reverse.value = true;
    _show.value = true;
    nextTick(() => {
      drawerANI.value?.play();
    });
  }

  //外部手动调用关闭方法
  function close() {
    reverse.value = false;
    overlayAni.value?.close();
    drawerANI.value?.play();
  }

  //外部调用的方法。
  defineExpose({
    close: close,
    open: open,
  });
</script>

<style scoped>
  .flex-left-custom {
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
  }

  .flex-right-custom {
    display: flex;
    justify-content: flex-start;
    align-items: flex-end;
  }

  .flex-top-custom {
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
  }

  .flex-end-custom {
    display: flex;
    justify-content: flex-end;
    align-items: flex-end;
  }

  .flex-center-custom {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: row;
  }
</style>
